/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "resize_command.h"

#include <cinttypes>
#include <cstring>
#include <unistd.h> 
#include <sstream>
#include <unordered_map>
#include <string_view>
#include "hmfs_utils.h"
#include "hmfs_common.h"
#include "hmfs_encoding.h"
namespace OHOS {
namespace Hmfs {
const struct option ResizeCmdParser::CMD_OPTS[] = {
    { "d", required_argument, nullptr, Option::DEBUG },
    { "V", no_argument, nullptr, Option::RVERSION},
    { "h", no_argument, nullptr, Option::HELP},
    { "f", no_argument, nullptr, Option::FORCEWRITE },
    { "t", required_argument, nullptr, Option::TARGETSECTOR},
    { "i", no_argument, nullptr, Option::EXNODEBITMAP},
    { "o", required_argument, nullptr, Option::OVERPROVISION},
    { "s", no_argument, nullptr, Option::SAFE},
    { "O", required_argument, nullptr, Option::FEATURELIST},
    { "C", required_argument, nullptr, Option::ENCODING},
    { 0, 0, 0, 0},
};

const std::string ResizeCmdParser::CMD_PARAMS = "d:fst:O:C:io:V";

ResizeCmdParser::ResizeCmdParser(CmdConfig &resizeConfig) : CmdParser(resizeConfig), resizeConfig_(resizeConfig)
{
    resizeConfig_.deviceList.resize(1);
    using namespace std::placeholders;
    RegCmdOption(Option::RVERSION, false, std::bind(&ResizeCmdParser::PrintVersion, this, _1));
    RegCmdOption(Option::HELP, false, std::bind(&ResizeCmdParser::ShowHelp, this, _1));
    // RegCmdOption(Option::RVERSION, false, [this](const std::string &) -> uint32_t { return PrintVersion(); });
    // RegCmdOption(Option::HELP, false, [this](const std::string &) -> uint32_t { return ShowHelp(); });
    RegCmdOption(Option::DEBUG, true, std::bind(&ResizeCmdParser::SetDebugLevel, this, _1));
    RegCmdOption(Option::TARGETSECTOR, true, std::bind(&ResizeCmdParser::SetTargetSectors, this, _1));
    RegCmdOption(Option::SAFE, false, std::bind(&ResizeCmdParser::SetSafe, this, _1));
    RegCmdOption(Option::FORCEWRITE, false, std::bind(&ResizeCmdParser::SetForce, this, _1));
    RegCmdOption(Option::ENCODING, true, std::bind(&ResizeCmdParser::SetEncoding, this, _1));
    RegCmdOption(Option::EXNODEBITMAP, false, std::bind(&ResizeCmdParser::SetExnodeBitmap, this, _1));
    RegCmdOption(Option::OVERPROVISION, true, std::bind(&ResizeCmdParser::SetOverProvision, this, _1));
    RegCmdOption(Option::FEATURELIST, true, std::bind(&ResizeCmdParser::SetFeatureList, this, _1));

    RegisterSingleton(this);
}

uint32_t ResizeCmdParser::Parse(int argc, char *argv[])
{
    if (ParseOption(argc, argv)) {
        return -1;
    }

    if (optind >= argc) {
        HMFS_ERROR("Device not specified");
        ShowCmdUsage();
        return -1;
    }
    resizeConfig_.deviceList[0] = argv[optind];
    // resizeConfig_.deviceList[0] = argv[optind];
    // if ((optind + 1) < argc) {
    //     if (resizeConfig_.deviceList.size() > 1) {
    //         HMFS_ERROR("Not support custom size on multi-devs");
    //         ShowCmdUsage();
    //         return -1;
    //     }
    //     resizeConfig_.totalSectors = atoll(argv[optind + 1]);
    // }

    // InitCommand();
    // if (ParseCommand(argc, argv) != RESTOOL_SUCCESS) {
    //     return RESTOOL_ERROR;
    // }
    // if (CheckParam() != RESTOOL_SUCCESS) {
    //     return RESTOOL_ERROR;
    // }

    return 0;
}

void ResizeCmdParser::ShowCmdUsage()
{
    std::cout << "\nUsage: resize.hmfs [options] device\n";
    std::cout << "[options]:\n";
    std::cout << "  -d debug level [default:0]\n";
    std::cout << "  -i extended node bitmap, node ratio is 20%% by default\n";
    std::cout << "  -o overprovision percentage [default:auto]\n";
    std::cout << "  -s safe resize (Does not resize metadata)\n";
    std::cout << "  -t target sectors [default: device size]\n";
    std::cout << "  -O feature1[,feature2,...] e.g. \"fsprojquota,fscasefold\"\n";
    std::cout << "  -C [encoding[:flag1,...]] Support casefolding with optional flags\n";
    std::cout << "  -V print the version number and exit\n";
}

ArgParseResult ResizeCmdParser::SetDebugLevel(const std::string &argValue)
{
    resizeConfig_.debugLevel = std::stoi(argValue);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetForce(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.force = 1;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetSafe(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.safeResize = 1;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetTargetSectors(const std::string &argValue)
{
    if (strncmp(argValue.c_str(), "0x", 2)) {
        sscanf(argValue.c_str(), "%" PRIu64"", &resizeConfig_.targetSectors);
    } else {
        sscanf(argValue.c_str(), "%" PRIu64"", &resizeConfig_.targetSectors);
    }
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::PrintVersion(const std::string& argValue)
{
    (void)argValue;
    std::cout << "Info: Hmfstool version= " << "1.0" << std::endl;
    exit(0);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::ShowHelp(const std::string& argValue)
{
    (void)argValue;
    ShowCmdUsage();
    exit(0);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetEncoding(const std::string &argValue)
{
    std::stringstream input(argValue);
    std::string encodingStr;
    std::getline(input, encodingStr, ':');
    if (EncodingStrToValue(encodingStr, resizeConfig_.sEncoding)) {
        HMFS_ERROR("Unknown encoding : %s", encodingStr.c_str());
        return ArgParseResult::ERROR;
    }
    std::string flagStr;
    if (std::getline(input, flagStr, ':')) {
        if (EncodingFlagStrToValue(flagStr, resizeConfig_.sEncodingFlags)) {
            HMFS_ERROR("Unknown flag : %s", flagStr.c_str());
            return ArgParseResult::ERROR;
        }
    } else {
        resizeConfig_.sEncodingFlags = GetDefaultEncodingFlag(resizeConfig_.sEncoding);
    }
    resizeConfig_.features |= HMFS_FEATURE_CASEFOLD;
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetOverProvision(const std::string &argValue)
{
    resizeConfig_.newOverprovision = std::stod(argValue);
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::SetExnodeBitmap(const std::string &argValue)
{
    (void)argValue;
    resizeConfig_.largeNatBitmap = 1;
    return ArgParseResult::OK;
}

std::vector<std::string> ResizeCmdParser::SplitStringList(const std::string& srcString, char delimiter)
{
    std::vector<std::string> list;
    std::stringstream strStream(srcString);
    std::string subString;
    while (std::getline(strStream, subString, delimiter)) {
        size_t start = subString.find_first_not_of(" ");
        if (start == std::string::npos) {
            continue;
        }
        size_t end = subString.find_last_not_of(" ");
        list.emplace_back(subString.substr(start, end - start + 1));
    }
    return list;
}

ArgParseResult ResizeCmdParser::SetFeatureList(const std::string &argValue)
{
    std::unordered_map<std::string, uint32_t> featureTable = {
        {"encrypt",                HMFS_FEATURE_ENCRYPT},
        {"extra_attr",             HMFS_FEATURE_EXTRA_ATTR},
        {"project_quota",          HMFS_FEATURE_PRJQUOTA},
        {"inode_checksum",         HMFS_FEATURE_INODE_CHKSUM},
        {"flexible_inline_xattr",  HMFS_FEATURE_FLEXIBLE_INLINE_XATTR},
        {"quota",                  HMFS_FEATURE_QUOTA_INO},
        {"inode_crtime",           HMFS_FEATURE_INODE_CRTIME},
        {"lost_found",             HMFS_FEATURE_LOST_FOUND},
        {"verity",                 HMFS_FEATURE_VERITY},
        {"sb_checksum",            HMFS_FEATURE_SB_CHKSUM},
        {"casefold",               HMFS_FEATURE_CASEFOLD},
        {"compression",            HMFS_FEATURE_COMPRESSION},
        {"ro",                     HMFS_FEATURE_RO},
    };

    std::vector<std::string> featureList = SplitStringList(argValue, ',');
    for (auto& feature : featureList) {
        HMFS_DEBUG("-O featureTrim: %s", feature.c_str());
        auto it = featureTable.find(feature);
        if (it == featureTable.end()) {
            HMFS_ERROR("Wrong features: %s", feature.c_str());
            return ArgParseResult::ERROR;
        }
        resizeConfig_.features |= it->second;
    }
    return ArgParseResult::OK;
}

ArgParseResult ResizeCmdParser::OptionAProcess(const std::string &argValue)
{
    auto value = std::stoi(argValue);
    resizeConfig_.heapBasedAllocation = (value != 0);
    return ArgParseResult::OK;
}

// void ResizeCmdParser::InitCommand()
// {
//     using namespace placeholders;
//     handles_.emplace(Option::VERSION, [this](const std::string &) -> uint32_t { return PrintVersion(); });
//     handles_.emplace(Option::HELP, [this](const std::string &) -> uint32_t { return ShowHelp(); });
//     handles_.emplace(Option::DEBUG, bind(&ResizeCmdParser::SetDebugLevel, this, _1));
//     handles_.emplace(Option::TARGETSECTOR, bind(&ResizeCmdParser::SetTargetSectors, this, _1));
// }

// uint32_t ResizeCmdParser::HandleProcess(int c, const std::string &argValue)
// {
//     auto handler = handles_.find(c);
//     if (handler == handles_.end()) {
//         cerr << "Error: unsupport " << c << endl;
//         return -1;
//     }
//     return handler->second(argValue);
// }

// uint32_t ResizeCmdParser::CheckError(int argc, char *argv[], int c, int optIndex)
// {
//     if (optIndex != -1) {
//         if ((optarg == nullptr && (optind - 1 < 0 || optind - 1 >= argc)) ||
//             (optarg != nullptr && (optind - 2 < 0 || optind - 2 >= argc))) { // 1 or 2 menas optind offset value
//             return -1;
//         }
//         std::string curOpt = (optarg == nullptr) ? argv[optind - 1] : argv[optind - 2];
//         if (curOpt != ("--" + string(CMD_OPTS[optIndex].name))) {
//             cerr << "Error: unknown option " << curOpt << endl;
//             return -1;
//         }
//     }
//     if (c == Option::UNKNOWN) {
//         if (optopt == 0 && (optind - 1 < 0 || optind - 1 >= argc)) {
//             return -1;
//         }
//         std::string optUnknown = (optopt == 0) ? argv[optind - 1] : ("-" + std::string(1, optopt));
//         cerr << "Error: unknown option " << optUnknown << endl;
//         return -1;
//     }
//     if (c == Option::NO_ARGUMENT) {
//         if (optind - 1 < 0 || optind - 1 >= argc) {
//             return -1;
//         }
//         if (IsLongOpt(argc, argv)) {
//             cerr << "Error: option " << argv[optind - 1] << " must have argument" << endl;
//         } else {
//             cerr << "Error: unknown option " << argv[optind - 1] << endl;
//         }
//         return -1;
//     }
//     return RESTOOL_SUCCESS;
// }

// uint32_t ResizeCmdParser::ParseCommand(int argc, char *argv[])
// {
//     restoolPath_ = std::string(argv[0]);
//     while (true) {
//         int optIndex = -1;
//         int c = getopt_long(argc, argv, CMD_PARAMS.c_str(), CMD_OPTS, &optIndex);
//         if (CheckError(argc, argv, c, optIndex) != 0) {
//             return -1;
//         }
//         if (c == Option::END) {
//             if (argc == optind) {
//                 break;
//             }
//             std::string errmsg = "Error: invalid arguments : ";
//             for (int i = optind; i < argc; i++) {
//                 errmsg.append(argv[i]).append(" ");
//             }
//             cerr << errmsg << endl;
//             return -1;
//         }

//         std::string argValue = (optarg != nullptr) ? optarg : "";
//         if (HandleProcess(c, argValue) != 0) {
//             return -1;
//         }
//     }
//     return 0;
// }

// bool ResizeCmdParser::IsLongOpt(int argc, char *argv[]) const
// {
//     if (optind - 1 < 0 || optind - 1 >= argc) {
//         return false;
//     }
//     for (auto iter : CMD_OPTS) {
//         if (optopt == iter.val && argv[optind - 1] == ("--" + std::string(iter.name))) {
//             return true;
//         }
//     }
//     return false;
// }

}
}
